package com.even.camerarecoder

import android.annotation.TargetApi
import android.media.MediaCodec
import android.media.MediaMuxer
import android.os.Build
import android.os.Environment
import android.text.format.DateFormat
import android.text.format.Formatter
import android.util.Log
import com.even.camerarecoder.aac.AACEncoder
import com.evendai.loglibrary.Timber
import java.io.File
import java.io.IOException
import java.util.concurrent.Executors

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
class H264ToMp4Saver {

    @Suppress("SpellCheckingInspection")
    private lateinit var mMediaMuxer: MediaMuxer
    private var mVideoTrackIndex: Int = 0
    private var mAudioTrackIndex: Int = 0
    @Suppress("SpellCheckingInspection")
    private var isMediaMuxerWriteData = false

    private val deleteThreadPool = Executors.newFixedThreadPool(1)

    /** 开始录Mp4的时间，从这个时间开始算，10分钟后保存这个视频重新再录   */
    private var recordVideoStartTime = 0L
    private var startTime = 0L
    private var mFrameCount = 0L
    @Suppress("SpellCheckingInspection")
    private var createMediaMuxerOk: Boolean = false
    /** 10分钟   */
//    private val tenMinutes = 1000L * 60 * 10
    private val tenMinutes = 1000L * 60 * 1

    /** SDCard路径 */
    private var mSdCardPath: String? = null
    /** 视频保存目录 */
    private lateinit var mVideoSaveDir: File
    /** 指示是否有SDCard  */
    private var hasSDCard = false
    private var mVideoFile: File? = null

    @Suppress("PrivatePropertyName")
    private val _1K: Long = 1024L
    @Suppress("PrivatePropertyName")
    private val _1M: Long = 1024L * _1K
    @Suppress("PrivatePropertyName")
    private val _300M: Long = 300L * _1M
    /** 表示已经调用了close()方法 */
    private var calledCloseMethod = false

    init {
        checkHasSDCard()

        if (hasSDCard) {
            // 不没有系统权限时，则使用下面的目录来存储视频，此目录不需要任何存储权限
            /*val movieDirs = App.sContext.getExternalFilesDirs(Environment.DIRECTORY_MOVIES)
            mVideoSaveDir = movieDirs[0]*/
            mVideoSaveDir = App.sContext.getExternalFilesDir(Environment.DIRECTORY_MOVIES)!!
            Timber.i("视频存储目录：${mVideoSaveDir.absolutePath}")
            /*mVideoSaveDir = if (movieDirs.isNullOrEmpty() || movieDirs.size < 2) {
                File(mSdCardPath, "/Android/data/${App.sContext.packageName}/files/Movies")
            } else {
                movieDirs[1] // 数组中的第一个条目被视为主要外部存储，即不可移除的。第二个则为SDCard，可移除的
            }*/

            Timber.i("${mSdCardPath}的总存储空间：${Formatter.formatFileSize(App.sContext, SDCardUtil.getSdCardTotalSpace(mSdCardPath))}")
            Timber.i("${mSdCardPath}的可用储空间：${Formatter.formatFileSize(App.sContext, SDCardUtil.getSdCardUsableSpace(mSdCardPath))}")

            /*mVideoSaveDir = File(mSdCardPath, "/DCIM/Video")

            if (!mVideoSaveDir.exists()) {
                if (!mVideoSaveDir.mkdirs()) {
                    Timber.e("创建视频保存目录失败!")
                    hasSDCard = false
                }
            }*/

            // 第一次在外置SDCard创建文件时花的时间比较长，第二次创建就快了，所以这里先创建一次，方便第二次创建时提速
            if (hasSDCard) {
                val testFile = File(mVideoSaveDir, "test.mp4")
                if (!testFile.exists()) {
                    if (testFile.createNewFile()) {
                        testFile.delete()
                    }
                } else {
                    testFile.delete()
                }
            }
        } else {
            Timber.fe("没有检测到SDCard")
        }
    }

    fun checkHasSDCard() {
        mSdCardPath = Environment.getExternalStorageDirectory().absolutePath
        hasSDCard = mSdCardPath != null
    }

    @Synchronized
    fun close() {
        if (calledCloseMethod) return
        calledCloseMethod = true
        saveMp4File()
    }

    /** 保存H264为Mp4  */
    @Synchronized
    fun h264ToMp4(muxerData: MuxerData) {
        if (!hasSDCard || calledCloseMethod) return // calledCloseMethod用于预防多线程时一个线程调用了close()，一个线程又正好又调用了h264ToMp4(MuxerData)

        if (!createMediaMuxerOk) {
            createMp4File()
            if (!createMediaMuxerOk) return
        }

        try {
            if (recordVideoStartTime == 0L) {
                recordVideoStartTime = System.currentTimeMillis() // 记录刚开始保存视频时的时间
                startTime = recordVideoStartTime
            }
            val recordMp4ElapsedTime = System.currentTimeMillis() - recordVideoStartTime
            if (recordMp4ElapsedTime >= tenMinutes && muxerData.bufferInfo.flags == MediaCodec.BUFFER_FLAG_KEY_FRAME) {
                // 条件中增加关键帧判断，是因为要让这上帧视频完整，当是关键帧时保存视频，但是这个关键帧并不写入此保存的视频，而是写入下一个视频。
                Log.i(TAG, "录制的视频大于了10分钟")
                saveMp4File()

                createMp4File()
                if (!createMediaMuxerOk) return
            }

            // 这个函数效率非常高，调用时间0.1毫秒 ~ 1毫秒之间
            //val start = System.nanoTime()
            mMediaMuxer.writeSampleData(
                if (muxerData.isVideoData) mVideoTrackIndex else mAudioTrackIndex,
                muxerData.byteBuffer,
                muxerData.bufferInfo
            )
            //Timber.i("混合音视频时间：${String .format("%.2f", (System.nanoTime() -  start) / 1000_000f)}")

            isMediaMuxerWriteData = true
            mFrameCount++
        } catch (e: Exception) {
            Timber.e(e,"写mp4数据时出现异常")
            checkHasSDCard()
            saveMp4File()
        }
    }

    /** 确保有足够的可用空间 */
    private fun ensureEnoughSpace() {
        if (SDCardUtil.getSdCardUsableSpace(mSdCardPath) < _300M) {
            deleteThreadPool.execute {
                while (SDCardUtil.getSdCardUsableSpace(mSdCardPath) < _300M) {
                    deleteOldestFile()
                }
            }
        }
    }

    private fun deleteOldestFile() {
        repeat(10) {    // 1次删除10个文件
            if (!mVideoSaveDir.exists()) return
            val listFiles = mVideoSaveDir.listFiles()
            if (listFiles.isNullOrEmpty()) return
            val oldestFile = listFiles.minByOrNull { it.lastModified() }!! // 修改时间最小的就是最老的
            val delete = oldestFile.delete()
            Timber.i("空间小于300M，删除最老的文件：${oldestFile.absolutePath},是否删除成功：$delete")
        }
    }

    private fun saveMp4File() {
        if (!hasSDCard) return

        releaseMediaMuxer()

        val elapsedTime = System.currentTimeMillis() - startTime
        if (mVideoFile?.exists() == true){
            val durationString = MediaUtil.getMediaDuration(mVideoFile!!.absolutePath) ?: "-1"
            var duration = 0
            try {
                duration = durationString.toInt()
            } catch (e: Exception) {
                Timber.e("文件时长转换为int时出现异常，durationString = $durationString")
            }

            if (duration < 3000) { // 小于3秒钟的视频不保存，直接删掉
                val delete = mVideoFile!!.delete()
                Timber.i("删除一个${duration}秒钟的视频，是否删除成功：$delete，文件为：${mVideoFile!!.absolutePath}")
            } else {
                val fps = if (mFrameCount == 0L) 0f else mFrameCount / (elapsedTime / 1000f)
                Timber.i("保存了一个视频文件，实际fps为：$fps，路径：${mVideoFile!!.absolutePath}")
            }
        }

        isMediaMuxerWriteData = false
        recordVideoStartTime = 0
        mFrameCount = 0
        startTime = 0
        mVideoFile = null
    }

    private fun createMp4File() {
        val audioMediaFormat = AACEncoder.mediaFormat
        val videoMediaFormat = H264Encoder.mediaFormat
        if (videoMediaFormat == null) {
            Timber.i("videoMediaFormat = null")
            return
        }
        if (!hasSDCard || audioMediaFormat == null) {
            Timber.e(Exception("没有创建Mp4文件，因为参数错误：hasSDCard = $hasSDCard, audioMediaFormat = $audioMediaFormat"))
            return
        }
        // 确保有足够的空间存储文件
        ensureEnoughSpace()

        val start = System.currentTimeMillis()
        val fileName = "${DateFormat.format("yyyy_MM_dd_kk_mm_ss", System.currentTimeMillis())}.mp4"
        mVideoFile = File(mVideoSaveDir, fileName)
        isMediaMuxerWriteData = false
        recordVideoStartTime = 0
        mFrameCount = 0
        startTime = 0
        try {
            mMediaMuxer = MediaMuxer(mVideoFile!!.absolutePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
            mVideoTrackIndex = mMediaMuxer.addTrack(videoMediaFormat) // 添加视频轨道
            mAudioTrackIndex = mMediaMuxer.addTrack(audioMediaFormat) // 添加音频轨道
            mMediaMuxer.start()
            createMediaMuxerOk = true
        } catch (e: IOException) {
            checkHasSDCard()
            createMediaMuxerOk = false
            @Suppress("SpellCheckingInspection")
            Timber.e(e,"MediaMuxer创建失败!")
            if (mVideoFile != null && mVideoFile!!.exists()) {
                val isDelete = mVideoFile!!.delete()
                Timber.e("生成了一个空文件，是否删除成功：$isDelete, 文件名为：${mVideoFile!!.absolutePath}")
                mVideoFile = null
            }
        }
        Timber.i("创建文件花的时间：${System.currentTimeMillis() - start}毫秒")
    }

    private fun releaseMediaMuxer() {
        if (!createMediaMuxerOk) {
            return
        }

        createMediaMuxerOk = false
        try {
            if (isMediaMuxerWriteData) {
                isMediaMuxerWriteData = false
                mMediaMuxer.stop() // 如果我们没有写入东西时就关闭就会抛出异常
            }
            mMediaMuxer.release()
        } catch (e: Exception) {
            @Suppress("SpellCheckingInspection")
            Timber.w(e,"别慌，正常关闭MediaMuxer时出现异常")
        }
    }

    companion object {
        private val TAG = H264ToMp4Saver::class.java.simpleName
    }
}